{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Kk2cahdPEsX-"
   },
   "source": [
    "# TensorFlow2教程-自定义训练实战\n",
    "\n",
    "本教程我们将使用TensorFlow来实现鸢尾花分类。整个过程包括：构建模型、模型训练、模型预测。\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "VEjExcBmPWNy"
   },
   "source": [
    "## 导入相关库\n",
    "导入TensorFlow和其他所需的Python模块。 默认情况下，TensorFlow2使用急切执行来程序，会立即返回结果。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "CDuwOUXPEkJs"
   },
   "outputs": [],
   "source": [
    "from __future__ import absolute_import, division, print_function, unicode_literals\n",
    "import os\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 71
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 31258,
     "status": "ok",
     "timestamp": 1564752585232,
     "user": {
      "displayName": "Will Chen",
      "photoUrl": "",
      "userId": "01179718990779759737"
     },
     "user_tz": -480
    },
    "id": "iIbJspJUQXtN",
    "outputId": "ffc0ced4-b615-4ed2-cda1-be2c2d3bf164"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[K     |████████████████████████████████| 79.9MB 429kB/s \n",
      "\u001b[K     |████████████████████████████████| 3.0MB 34.9MB/s \n",
      "\u001b[K     |████████████████████████████████| 419kB 55.7MB/s \n",
      "\u001b[?25h"
     ]
    }
   ],
   "source": [
    "!pip install -q tensorflow==2.0.0-alpha0\n",
    "import tensorflow as tf"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 53
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 962,
     "status": "ok",
     "timestamp": 1564752800388,
     "user": {
      "displayName": "Will Chen",
      "photoUrl": "",
      "userId": "01179718990779759737"
     },
     "user_tz": -480
    },
    "id": "p7YBIBwCQii3",
    "outputId": "b7c7223f-09c6-40d1-c2fc-7eafaa1e1b33"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf version: 2.0.0-alpha0\n",
      "eager execution: True\n"
     ]
    }
   ],
   "source": [
    "print('tf version:', tf.__version__)\n",
    "print('eager execution:', tf.executing_eagerly())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 323
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 2381,
     "status": "ok",
     "timestamp": 1564752879806,
     "user": {
      "displayName": "Will Chen",
      "photoUrl": "",
      "userId": "01179718990779759737"
     },
     "user_tz": -480
    },
    "id": "KN00YNtIh4-I",
    "outputId": "b526fcab-9223-49d0-b029-1b36b84f1d8d"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Fri Aug  2 13:34:38 2019       \n",
      "+-----------------------------------------------------------------------------+\n",
      "| NVIDIA-SMI 418.67       Driver Version: 410.79       CUDA Version: 10.0     |\n",
      "|-------------------------------+----------------------+----------------------+\n",
      "| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |\n",
      "| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |\n",
      "|===============================+======================+======================|\n",
      "|   0  Tesla T4            Off  | 00000000:00:04.0 Off |                    0 |\n",
      "| N/A   47C    P8    15W /  70W |      0MiB / 15079MiB |      0%      Default |\n",
      "+-------------------------------+----------------------+----------------------+\n",
      "                                                                               \n",
      "+-----------------------------------------------------------------------------+\n",
      "| Processes:                                                       GPU Memory |\n",
      "|  GPU       PID   Type   Process name                             Usage      |\n",
      "|=============================================================================|\n",
      "|  No running processes found                                                 |\n",
      "+-----------------------------------------------------------------------------+\n"
     ]
    }
   ],
   "source": [
    "!nvidia-smi"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "PasatZ2jR441"
   },
   "source": [
    "## 鸢尾花分类问题\n",
    "\n",
    "想象一下，你是一名植物学家，正在寻找一种自动化的方法来对你找到的每种鸢尾花进行分类。 机器学习提供了许多算法来对花进行统计分类。 例如，复杂的机器学习程序可以基于照片对花进行分类。而这里，我们将根据萼片和花瓣的长度和宽度测量来对鸢尾花进行分类。\n",
    "\n",
    "鸢尾花有300多种类别，但我们的这里主要对以下三种进行分类：\n",
    "- Iris setosa\n",
    "- Iris virginica\n",
    "- Iris versicolor\n",
    "# ![](https://www.tensorflow.org/images/iris_three_species.jpg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Y7yJD48rTLtp"
   },
   "source": [
    "幸运的是，有人已经用萼片和花瓣测量创建了120个鸢尾花的数据集。 这是一个流行的初学者机器学习分类问题的经典数据集。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "XoBuC7GGTO1f"
   },
   "source": [
    "下载数据集\n",
    "使用tf.keras.utils.get_file函数下载训练数据集文件。 这将返回下载文件的文件路径。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 6480,
     "status": "ok",
     "timestamp": 1559464545246,
     "user": {
      "displayName": "Will Chen",
      "photoUrl": "",
      "userId": "01179718990779759737"
     },
     "user_tz": -480
    },
    "id": "BpGa5eF7RDUF",
    "outputId": "222762a0-754f-494a-eab7-624e28a60f3e"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "下载数据至： /root/.keras/datasets/iris_training.csv\n"
     ]
    }
   ],
   "source": [
    "train_dataset_url = \"https://storage.googleapis.com/download.tensorflow.org/data/iris_training.csv\"\n",
    "train_dataset_fp = tf.keras.utils.get_file(fname=os.path.basename(train_dataset_url),\n",
    "                                          origin=train_dataset_url)\n",
    "print('下载数据至：', train_dataset_fp)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "xOH2I3MKUWa8"
   },
   "source": [
    "## 检查数据\n",
    "此数据集iris_training.csv是一个纯文本文件，用于存储格式为逗号分隔值（CSV）的表格数据。 使用head -n5命令在前五个条目中取一个峰值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 130
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 9637,
     "status": "ok",
     "timestamp": 1559464548420,
     "user": {
      "displayName": "Will Chen",
      "photoUrl": "",
      "userId": "01179718990779759737"
     },
     "user_tz": -480
    },
    "id": "Zccn4y_mUKt3",
    "outputId": "75d514b3-1426-4184-e899-64b0db98988e"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "120,4,setosa,versicolor,virginica\n",
      "6.4,2.8,5.6,2.2,2\n",
      "5.0,2.3,3.3,1.0,1\n",
      "4.9,2.5,4.5,1.7,2\n",
      "4.9,3.1,1.5,0.1,0\n"
     ]
    }
   ],
   "source": [
    "!head -n5 {train_dataset_fp}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "DX1RYV49X3xk"
   },
   "source": [
    "从数据集的此视图中，请注意以下内容：\n",
    "\n",
    "第一行是包含有关数据集的信息的标题：\n",
    "总共有120个例子。 每个示例都有四个特征和三个可能的标签名称之一。\n",
    "后续行是数据记录，每行一个示例，其中：\n",
    "前四个字段是特征：这些是示例的特征。 这里，字段包含代表花卉测量值的浮点数。\n",
    "最后一列是标签：这是我们想要预测的值。 对于此数据集，它是与花名称对应的整数值0,1或2。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "TfXADTDqUpEC"
   },
   "outputs": [],
   "source": [
    "column_names = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species']\n",
    "# 获取特征和标签名\n",
    "feature_name = column_names[:-1]\n",
    "label_name = column_names[-1]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "2h2J2is4ZUOH"
   },
   "source": [
    "每个标签都与字符串名称相关联（例如，“setosa”），但机器学习通常依赖于数值。使用标签数字来映射类别，例如：\n",
    "\n",
    "- 0：Iris setosa:\n",
    "- 1：Iris versicolor\n",
    "- 2：Iris virginica\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "ugETL6NbZFGW"
   },
   "outputs": [],
   "source": [
    "class_names = ['Iris setosa', 'Iris versicolor', 'Iris virginica']"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "qpv35o3lZ587"
   },
   "source": [
    "## 创建一个 tf.data.Dataset\n",
    "TensorFlow的数据集API处理许多将数据加载到模型中的常见情况。这是一个高级API，用于读取数据并将其转换为用于训练的数据类型。\n",
    "\n",
    "由于数据集是CSV格式的文本文件，因此需要使用make_csv_dataset函数将数据解析为合适的格式。由于此函数为训练模型生成数据，因此默认行为是对数据（shuffle=True, shuffle_buffer_size=10000）进行混洗，并永远重复数据集（num_epochs=None）。同时还需要设置batch_size参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "N3ZHbT89Z22R"
   },
   "outputs": [],
   "source": [
    "batch_size=32\n",
    "train_dataset = tf.data.experimental.make_csv_dataset(\n",
    "    train_dataset_fp,\n",
    "    batch_size,\n",
    "    column_names=column_names,\n",
    "    label_name=label_name,\n",
    "    num_epochs=1\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "cFO6ovo3bOHO"
   },
   "source": [
    "该make_csv_dataset函数返回tf.data.Dataset的(features, label)对，其中features是一个字典：{'feature_name': value}\n",
    "\n",
    "而这些Dataset对象是可迭代的。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 266
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 9571,
     "status": "ok",
     "timestamp": 1559464548444,
     "user": {
      "displayName": "Will Chen",
      "photoUrl": "",
      "userId": "01179718990779759737"
     },
     "user_tz": -480
    },
    "id": "H9NpQ-U1bEqC",
    "outputId": "356b3b5e-941c-486d-fab5-b0ed9a2088d1"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "OrderedDict([('sepal_length', <tf.Tensor: id=64, shape=(32,), dtype=float32, numpy=\n",
      "array([7.6, 6.9, 7.2, 5. , 6.7, 4.8, 5.4, 5.1, 7.7, 6. , 6.3, 7.4, 5.2,\n",
      "       7.2, 6.7, 6.1, 5. , 4.9, 6.2, 4.5, 6.6, 6. , 5.5, 6.3, 4.8, 6.7,\n",
      "       6.1, 5.6, 7.3, 6.9, 5.7, 6.3], dtype=float32)>), ('sepal_width', <tf.Tensor: id=65, shape=(32,), dtype=float32, numpy=\n",
      "array([3. , 3.2, 3.6, 2.3, 3. , 3. , 3.9, 3.7, 3. , 2.2, 2.3, 2.8, 2.7,\n",
      "       3.2, 3.1, 2.8, 3.4, 3.1, 2.8, 2.3, 3. , 3. , 3.5, 3.3, 3.4, 3. ,\n",
      "       2.8, 2.9, 2.9, 3.1, 3.8, 2.5], dtype=float32)>), ('petal_length', <tf.Tensor: id=62, shape=(32,), dtype=float32, numpy=\n",
      "array([6.6, 5.7, 6.1, 3.3, 5.2, 1.4, 1.3, 1.5, 6.1, 5. , 4.4, 6.1, 3.9,\n",
      "       6. , 5.6, 4. , 1.6, 1.5, 4.8, 1.3, 4.4, 4.8, 1.3, 6. , 1.6, 5. ,\n",
      "       4.7, 3.6, 6.3, 4.9, 1.7, 5. ], dtype=float32)>), ('petal_width', <tf.Tensor: id=63, shape=(32,), dtype=float32, numpy=\n",
      "array([2.1, 2.3, 2.5, 1. , 2.3, 0.3, 0.4, 0.4, 2.3, 1.5, 1.3, 1.9, 1.4,\n",
      "       1.8, 2.4, 1.3, 0.4, 0.1, 1.8, 0.3, 1.4, 1.8, 0.2, 2.5, 0.2, 1.7,\n",
      "       1.2, 1.3, 1.8, 1.5, 0.3, 1.9], dtype=float32)>)])\n"
     ]
    }
   ],
   "source": [
    "features, labels = next(iter(train_dataset))\n",
    "print(features)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "_TuigCMIb1xh"
   },
   "source": [
    "相同的特征被放在同一个数组中，数组维度为batch_size大小。\n",
    "可以可视化如图："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 283
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 9551,
     "status": "ok",
     "timestamp": 1559464548445,
     "user": {
      "displayName": "Will Chen",
      "photoUrl": "",
      "userId": "01179718990779759737"
     },
     "user_tz": -480
    },
    "id": "cvCg8fVubdx8",
    "outputId": "87d9beb3-65e6-49b6-bbce-bedec47f9725"
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "tags": []
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.scatter(features['petal_length'],\n",
    "            features['sepal_length'],\n",
    "            c=labels,\n",
    "            cmap='viridis')\n",
    "\n",
    "plt.xlabel(\"Petal length\")\n",
    "plt.ylabel(\"Sepal length\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "OCfiG8Tz712X"
   },
   "source": [
    "一般我们会把同一个数据的不同feature放在同一个数组中，我们使用tf.pack()來将features重构为(batch_size, num_features)形状。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "yXhTfd8PcGI4"
   },
   "outputs": [],
   "source": [
    "def pack_features_vector(features, labels):\n",
    "    features = tf.stack(list(features.values()), axis=1)\n",
    "    return features, labels"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 118
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 9528,
     "status": "ok",
     "timestamp": 1559464548458,
     "user": {
      "displayName": "Will Chen",
      "photoUrl": "",
      "userId": "01179718990779759737"
     },
     "user_tz": -480
    },
    "id": "5iz9qWly9hfO",
    "outputId": "facd97d3-5d88-43fd-ea48-b37eb55c6b6e"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor(\n",
      "[[7.6 3.  6.6 2.1]\n",
      " [6.9 3.2 5.7 2.3]\n",
      " [7.2 3.6 6.1 2.5]\n",
      " [5.  2.3 3.3 1. ]\n",
      " [6.7 3.  5.2 2.3]], shape=(5, 4), dtype=float32)\n"
     ]
    }
   ],
   "source": [
    "# 使用tf.data.Dataset.map将重构函数运用到每条数据中。\n",
    "train_dataset = train_dataset.map(pack_features_vector)\n",
    "# 查看前5个数据\n",
    "features, labels = next(iter(train_dataset))\n",
    "print(features[:5])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "MHiac-2m_jMP"
   },
   "source": [
    "## 选择模型\n",
    "\n",
    "一个模型是功能和标签之间的关系。对于鸢尾花分类问题，该模型定义了萼片和花瓣测量与预测的鸢尾花物种之间的关系。一些简单的模型可以用几行代数来描述，但是复杂的机器学习模型具有很多难以概括的参数。\n",
    "\n",
    "我们能否在不使用机器学习的情况下确定四种特征与虹膜物种之间的关系？也就是说，可以使用传统的编程技术（例如，很多条件语句）来创建模型吗？也许 - 如果你对数据集进行了足够长的分析，以确定特定物种的花瓣和萼片测量值之间的关系。这对于更复杂的数据集来说变得困难 - 可能是不可能的。良好的机器学习方法可以为我们确定模型。如果我们将足够的代表性示例提供给正确的机器学习模型类型，程序将为我们找出关系。\n",
    "\n",
    "选择具体模型\n",
    "\n",
    "我们需要选择要训练的模型。有很多类型的模型和挑选一个好的经验。本教程使用神经网络来解决鸢尾花I分类问题。神经网络可以找到特征和标签之间的复杂关系。它是一个高度结构化的图，组织成一个或多个隐藏层。每个隐藏层由一个或多个神经元组成。有几类神经网络，这个程序使用密集或完全连接的神经网络：一层中的神经元接收来自每个神经网络的输入连接上一层的神经元。例如，图2说明了一个由输入层，两个隐藏层和一个输出层组成的密集神经网络：\n",
    "\n",
    "![](https://www.tensorflow.org/images/custom_estimators/full_network.png)\n",
    "图2.具有特征，隐藏层和预测的神经网络。\n",
    " \n",
    "当对来自图2的模型进行训练并喂入未标记的示例时，它产生三个预测：该花是给定的鸢尾花物种的可能性。这种预测称为推理。对于此示例，输出预测的总和为1.0。在图2中，该预测分解为：0.02对山鸢尾，0.95对于变色鸢尾，并0.03为锦葵鸢尾。这意味着模型以95％的概率预测 - 未标记的示例花是变色鸢尾。\n",
    "\n",
    "##使用Keras创建模型\n",
    "\n",
    "TensorFlow tf.keras API是创建模型和图层的首选方式。这使得构建模型和实验变得容易，而Keras处理将所有内容连接在一起的复杂性。\n",
    "\n",
    "该tf.keras.Sequential模型是层的线性堆栈。它的构造函数采用一个层实例列表，在这种情况下，两个Dense层各有10个节点，一个输出层有3个节点代表我们的标签预测。第一层的input_shape参数对应于数据集中的要素数，并且是必需的。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "bfEEzsDK-7qS"
   },
   "outputs": [],
   "source": [
    "# 构建线性模型\n",
    "model = tf.keras.Sequential([\n",
    "    tf.keras.layers.Dense(10, activation='relu',input_shape=(4,)),\n",
    "    tf.keras.layers.Dense(10, activation='relu'),\n",
    "    tf.keras.layers.Dense(3)\n",
    "])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "OQQUlt7nN9k4"
   },
   "source": [
    "激活函数确定在层中的每个节点的输出形状。这些非线性很重要 - 没有它们，模型将等同于单个层。有许多可用的激活，但ReLU对于隐藏层是常见的。\n",
    "\n",
    "隐藏层和神经元的理想数量取决于问题和数据集。像机器学习的许多方面一样，选择神经网络的最佳形状需要知识和实验经验。根据经验，增加隐藏层和神经元的数量通常会创建一个更强大的模型，这需要更多的数据来有效地训练。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "d8JrLagJOggc"
   },
   "source": [
    "## 测试模型结构"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 118
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 10134,
     "status": "ok",
     "timestamp": 1559464549092,
     "user": {
      "displayName": "Will Chen",
      "photoUrl": "",
      "userId": "01179718990779759737"
     },
     "user_tz": -480
    },
    "id": "p3lKRc3ZN0s6",
    "outputId": "7a1b8c8a-0b24-471a-dc90-5f9d757436a8"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<tf.Tensor: id=229, shape=(5, 3), dtype=float32, numpy=\n",
       "array([[1.6543204 , 0.12405288, 0.24490094],\n",
       "       [1.4488522 , 0.11291474, 0.24872684],\n",
       "       [1.5161525 , 0.11867774, 0.28899187],\n",
       "       [0.86002606, 0.05858952, 0.06260413],\n",
       "       [1.3767202 , 0.10884094, 0.21688706]], dtype=float32)>"
      ]
     },
     "execution_count": 14,
     "metadata": {
      "tags": []
     },
     "output_type": "execute_result"
    }
   ],
   "source": [
    "prediction = model(features)\n",
    "prediction[:5]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "-1DoLLc7Os-p"
   },
   "source": [
    "多分类任务需要使用softmax进行归一化"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 118
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 10116,
     "status": "ok",
     "timestamp": 1559464549095,
     "user": {
      "displayName": "Will Chen",
      "photoUrl": "",
      "userId": "01179718990779759737"
     },
     "user_tz": -480
    },
    "id": "R3TrHwRSOqaN",
    "outputId": "017c168e-6971-4864-b666-219bb048de72"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<tf.Tensor: id=235, shape=(5, 3), dtype=float32, numpy=\n",
       "array([[0.6845738 , 0.148195  , 0.16723117],\n",
       "       [0.63935834, 0.16809471, 0.19254689],\n",
       "       [0.6492055 , 0.16049689, 0.1902975 ],\n",
       "       [0.52654505, 0.23625231, 0.23720267],\n",
       "       [0.62697244, 0.1764475 , 0.19658   ]], dtype=float32)>"
      ]
     },
     "execution_count": 15,
     "metadata": {
      "tags": []
     },
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tf.nn.softmax(prediction)[:5]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "NNK0BI0VPMY_"
   },
   "source": [
    "使用tf.argmax获取概率最大的类标签"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 82
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 10095,
     "status": "ok",
     "timestamp": 1559464549097,
     "user": {
      "displayName": "Will Chen",
      "photoUrl": "",
      "userId": "01179718990779759737"
     },
     "user_tz": -480
    },
    "id": "AdhaPPeaO5zZ",
    "outputId": "a2a16c38-8f75-4bb0-c94b-785327eff249"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "prediction: tf.Tensor([0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0], shape=(32,), dtype=int64)\n",
      "label: tf.Tensor([2 2 2 1 2 0 0 0 2 2 1 2 1 2 2 1 0 0 2 0 1 2 0 2 0 1 1 1 2 1 0 2], shape=(32,), dtype=int32)\n"
     ]
    }
   ],
   "source": [
    "print('prediction:', tf.argmax(prediction, axis=1))\n",
    "print('label:', labels)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "IalhdqdgCo2m"
   },
   "source": [
    "# 训练模型\n",
    "训练是机器学习中模型从数据集中学习知识并优化自身能力的过程。\n",
    "\n",
    "Iris分类问题是一个典型的监督学习问题，其通过包含标签的数据集进行学习。而无监督学习则是仅从特征中去寻找相应的模式。\n",
    "\n",
    "训练和评估的过程都需要计算模型的损失，它可以衡量预测与正确标签的差距，训练过程都是要最小化损失。\n",
    "\n",
    "我们后面将直接使用tf.keras里面包装好的损失函数来计算损失。\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "_CPDyoTLP1sb"
   },
   "outputs": [],
   "source": [
    "# 损失函数\n",
    "loss_object=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 10063,
     "status": "ok",
     "timestamp": 1559464549101,
     "user": {
      "displayName": "Will Chen",
      "photoUrl": "",
      "userId": "01179718990779759737"
     },
     "user_tz": -480
    },
    "id": "WpYuT9YKEiQn",
    "outputId": "c1bf906e-7f47-449a-a4ff-f8c1ee00af3f"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tf.Tensor(1.3738844, shape=(), dtype=float32)\n"
     ]
    }
   ],
   "source": [
    "# 获取损失\n",
    "def loss(model, x, y):\n",
    "    y_ = model(x)\n",
    "    return loss_object(y_true=y, y_pred=y_)\n",
    "l = loss(model, features, labels)\n",
    "print(l)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Xfccc9znFC2h"
   },
   "source": [
    "使用tf.GradientTape计算loss对所有变量的梯度。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "oa6VTHxjE885"
   },
   "outputs": [],
   "source": [
    "def grad(model, inputs, targets):\n",
    "    with tf.GradientTape() as tape:\n",
    "        loss_value = loss(model, inputs, targets)\n",
    "    return loss_value, tape.gradient(loss_value, model.trainable_variables)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "DFQv_sk3OlHV"
   },
   "source": [
    "## 创建优化器\n",
    "\n",
    "优化程序将计算出的梯度应用于模型的变量，以最大限度地减少损失函数。 您可以将损失函数视为曲面（参见图3），我们希望通过四处走动找到它的最低点。 渐变指向最陡的上升方向 - 所以我们将以相反的方向行进并向下移动。 通过迭代计算每批的损失和梯度，我们将在训练期间调整模型。 逐渐地，该模型将找到权重和偏差的最佳组合，以最小化损失。 损失越低，模型的预测越好。\n",
    "\n",
    "![](https://cs231n.github.io/assets/nn3/opt1.gif)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "dr_qJLJcPM5k"
   },
   "source": [
    "TensorFlow有许多可用于训练的优化算法。 该模型使用tf.train.GradientDescentOptimizer实现随机梯度下降（SGD）算法。 learning_rate设置每次迭代下一步的步长。 这是一个超参数，您通常会调整以获得更好的结果。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "rALMMe6lGKso"
   },
   "outputs": [],
   "source": [
    "optimizer = tf.keras.optimizers.Adam(learning_rate=0.01)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "qzEiEMNgP49Q"
   },
   "source": [
    "优化器使用如下"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 50
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 10030,
     "status": "ok",
     "timestamp": 1559464549111,
     "user": {
      "displayName": "Will Chen",
      "photoUrl": "",
      "userId": "01179718990779759737"
     },
     "user_tz": -480
    },
    "id": "3MiDlfWhPxVI",
    "outputId": "22ba84b7-e7f5-4b1a-81aa-05f392c46c3e"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "步数：0, 初始loss值：1.3738844394683838\n",
      "步数：1, loss值：1.1648454666137695\n"
     ]
    }
   ],
   "source": [
    "loss_value, grads = grad(model, features, labels)\n",
    "print('步数：{}, 初始loss值：{}'.format(optimizer.iterations.numpy(),\n",
    "                                loss_value.numpy()))\n",
    "optimizer.apply_gradients(zip(grads, model.trainable_variables))\n",
    "print('步数：{}, loss值：{}'.format(optimizer.iterations.numpy(),\n",
    "                                loss(model,features, labels).numpy()))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "SRxS5eX9R7DN"
   },
   "source": [
    "## 训练循环\n",
    "每个epoch数据将会被训练一次。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 101
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 18329,
     "status": "ok",
     "timestamp": 1559464557422,
     "user": {
      "displayName": "Will Chen",
      "photoUrl": "",
      "userId": "01179718990779759737"
     },
     "user_tz": -480
    },
    "id": "yvgUj4tzRO2r",
    "outputId": "bfc1c5ca-cc43-4bb2-dc71-418cbfca8fe3"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 000: Loss: 1.048, Accuracy: 70.000%\n",
      "Epoch 050: Loss: 0.074, Accuracy: 99.167%\n",
      "Epoch 100: Loss: 0.059, Accuracy: 99.167%\n",
      "Epoch 150: Loss: 0.054, Accuracy: 99.167%\n",
      "Epoch 200: Loss: 0.051, Accuracy: 99.167%\n"
     ]
    }
   ],
   "source": [
    "# 保存loss和acc\n",
    "train_loss_results=[]\n",
    "train_accuracy_results=[]\n",
    "\n",
    "num_epochs =201\n",
    "for epoch in range(num_epochs):\n",
    "    # 用于记录loss和acc的类\n",
    "    epoch_loss_avg = tf.keras.metrics.Mean()\n",
    "    epoch_accuracy = tf.keras.metrics.SparseCategoricalAccuracy()\n",
    "    \n",
    "    # 训练循环\n",
    "    for x, y in train_dataset:\n",
    "        # 获取loss和梯度\n",
    "        loss_value, grads = grad(model, x, y)\n",
    "        # 梯度优化\n",
    "        optimizer.apply_gradients(zip(grads, model.trainable_variables))\n",
    "        \n",
    "        # 记录loss均值\n",
    "        epoch_loss_avg(loss_value)\n",
    "        # 记录准确率\n",
    "        epoch_accuracy(y, model(x))\n",
    "\n",
    "    # 保存每个epoch的loss和acc\n",
    "    train_loss_results.append(epoch_loss_avg.result())\n",
    "    train_accuracy_results.append(epoch_accuracy.result())\n",
    "\n",
    "    if epoch % 50 == 0:\n",
    "        print(\"Epoch {:03d}: Loss: {:.3f}, Accuracy: {:.3%}\".format(epoch,\n",
    "                                                                    epoch_loss_avg.result(),\n",
    "                                                                    epoch_accuracy.result()))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "o_g5s0hOVTDj"
   },
   "source": [
    "## 可视化训练过程\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 582
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 1122,
     "status": "ok",
     "timestamp": 1559464652806,
     "user": {
      "displayName": "Will Chen",
      "photoUrl": "",
      "userId": "01179718990779759737"
     },
     "user_tz": -480
    },
    "id": "UggcRc8OUqqA",
    "outputId": "f2ffee13-3df4-475e-808e-597256ebe9ca"
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAt4AAAIdCAYAAAAK6HpFAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4zLCBo\ndHRwOi8vbWF0cGxvdGxpYi5vcmcvnQurowAAIABJREFUeJzs3XmcXXV9//HX597Zsk0SSAJZIAEM\nS1BciKjFVlurLFaoy8+KWrXtr/xsxW7WX7G1Lljb2lbb+ita0fKj+FMp1bbSmrpUcWkVJVZFFoEQ\nEJKwDGQh69y5935/f9wzM3cmk+QOuXPuMq/n4zGPnPM933POZ27w4ft+8z3fEyklJEmSJM2sQqsL\nkCRJkmYDg7ckSZKUA4O3JEmSlAODtyRJkpQDg7ckSZKUA4O3JEmSlAODtyTlLCKKEbEnIk5sZt92\nFhEnR8SeVtchSa1k8JakI8iC7+hPNSL21+2/ZrrXSylVUkrzU0r3N7PvdEXEH0VEiog3TWp/S9b+\n9gavsyUinn+4PimlzSml+UdRriR1PIO3JB1BFnznZ8HxfuAldW2fmNw/Inryr/IJuwt43aS212Xt\nTdFhn4ckzRiDtyQdpWzk+B8i4lMRsRt4bUQ8JyJuioidEfFgRHwwInqz/j3ZiPKabP//Zcf/PSJ2\nR8S3IuKk6fbNjl8QEXdFxK6I+D8R8V8R8YbDlP8t4JiIOC07/2nU/r/he5N+x4si4gfZ7/OfEfHk\nrP1TwArg37N/AfidiHhSVvMvRcT9wBdH2+qud2xEXJN9Njsi4jNZ+7KI2JDdZ3tEfP0J/8VIUpsx\neEtSc7wU+CSwEPgHoAz8JrAEOBc4H/hfhzn/1cAfAsdQG1V/z3T7RsQy4Hrgrdl97wXOaaD2jzM+\n6v064Nr6gxHxTOCjwP8EjgWuBj4bEX0ppUuAbcAF2b8AfKDu1J8CTgdePMU9Pwn0AeuAZcBfZ+1v\nBTYDS4HjgYamu0hSJzB4S1Jz/GdK6V9TStWU0v6U0s0ppW+nlMoppc3AVcDzDnP+p1NKG1NKI8An\ngKc9gb4/B3w/pfTZ7NhfAo82UPvHgddkI/K/kF2z3qXAh7LfqZJSujprf+YRrvvOlNK+lNL++saI\nOAF4AfBrKaUdKaWRlNLoyPYItRH0E1NKpbp2Sep4Bm9Jao4H6nci4vSI+FxEPBQRjwNXUBuFPpSH\n6rb3AYd7EPFQfVfU15FSSsCWIxWeUrqX2sj5HwO3ppS2TeqyGvi9bPrHzojYCSwHVh7h0g8cov0E\n4NGU0q4pjv0p8GPgyxFxT0S89Uj1S1KnMHhLUnOkSfsfAW4FnpRSGgTeAcQM1/AgsGp0JyKCI4fj\nUdcCb2HSNJPMA8C7U0qL6n7mppSuz45P/t1rjbXgP5UHgCURMTjFOY+nlH47pbQG+Hlqgf9w/1Ig\nSR3D4C1JM2MBsAvYGxFncPj53c3yb8AzIuIl2Uoiv0ltrnQjPgm8CPjMFMc+CrwpIp4ZNfOze8zL\njj8MnNxokSmlB4D/AK6MiEUR0RsRPwWQXfeU7EvDLqACVBu9tiS1M4O3JM2MtwCvB3ZTG/3+h5m+\nYUrpYWpztD8APAacQm11kuEGzt2XUvqPlNKBKY7dBPwa8GFgB7WlBl9b1+WPgXdn01B+q8FyR8+/\ni1pwf3O2fxrwFWAP8F/AX6eUvtHgNSWprcWh/yVQktTJIqJIbcWRVxheJan1HPGWpC4SEedn0zf6\nqS05OAJ8p8VlSZIweEtSt3kutXWwh4DzgJemlI441USSNPOcaiJJkiTlwBFvSZIkKQcGb0mSJCkH\nBm9JkiQpBwZvSZIkKQcGb0mSJCkHBm9JkiQpBwZvSZIkKQcGb0mSJCkHBm9JkiQpBwZvSZIkKQcG\nb0mSJCkHBm9JkiQpBwZvSZIkKQcGb0mSJCkHBm9JkiQpBwZvSZIkKQcGb0mSJCkHBm9JkiQpBwZv\nSZIkKQcGb0mSJCkHBm9JkiQpBwZvSZIkKQcGb0mSJCkHBm9JkiQpBwZvSZIkKQcGb0mSJCkHBm9J\nkiQpBwZvSZIkKQcGb0mSJCkHBm9JkiQpBwZvSZIkKQcGb0mSJCkHBm9JkiQpBwZvSZIkKQcGb0mS\nJCkHBm9JkiQpBwZvSZIkKQcGb0mSJCkHBm9JkiQpBwZvSZIkKQcGb0mSJCkHBm9JkiQpBwZvSZIk\nKQc9rS5gpixZsiStWbOm1WVIkiSpy333u999NKW09Ej9ujZ4r1mzho0bN7a6DEmSJHW5iPhxI/2c\naiJJkiTlwOAtSZIk5cDgLUmSJOXA4C1JkiTlwOAtSZIk5cDgLUmSJOXA4N1kr/3Yt/mTDXe0ugxJ\nkiS1GYN3kz22t8SmR/a0ugxJkiS1GYN3kx032M8ju4dbXYYkSZLajMG7yZYt6Ofhxw+0ugxJkiS1\n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\n+twAvD7bfgXwlZRSyrFGSZIk6ai0fKpJNmf7MuALQBG4OqV0W0RcAWxMKd0A/B3w8YjYBGynFs7b\nWcunu3QYP6/p8fOaHj+v6fHzmh4/r+nx85o+P7PpaevPKxw4liRJkmZeO0w1kSRJkrqewVuSJEnK\ngcFbkiRJyoHBW5IkScqBwVuSJEnKgcFbkiRJyoHBW5IkScqBwVuSJEnKgcFbkiRJyoHBW5IkScqB\nwVuSJEnKgcFbkiRJyoHBW5IkScqBwVuSJEnKgcFbkiRJyoHBW5IkScqBwVuSJEnKgcFbkiRJyoHB\nW5IkScqBwVuSJEnKgcFbkiRJyoHBW5IkScqBwVuSJEnKgcFbkiRJyoHBW5IkScqBwVuSJEnKgcFb\nkiRJyoHBW5IkScqBwVuSJEnKgcFbkiRJyoHBW5IkScqBwVuSJEnKgcFbkiRJykFPqwuYKUuWLElr\n1qxpdRmSJEnqct/97ncfTSktPVK/3IJ3RFwN/BzwSErpyVMcD+CvgQuBfcAbUkr/nR17PfD2rOsf\npZT+/kj3W7NmDRs3bmxW+ZIkSdKUIuLHjfTLc6rJNcD5hzl+AbA2+7kU+DBARBwDvBN4FnAO8M6I\nWDyjlUqSJElNllvwTil9Hdh+mC4XA9emmpuARRGxHDgP+FJKaXtKaQfwJQ4f4CVJkqS2004PV64E\nHqjb35K1Har9IBFxaURsjIiNQ0NDM1aoJEmSNF3tFLyPWkrpqpTS+pTS+qVLjzi/XZIkScpNOwXv\nrcAJdfursrZDtUuSJEkdo52C9w3A66Lm2cCulNKDwBeAF0XE4uyhyhdlbZIkSVLHyHM5wU8BzweW\nRMQWaiuV9AKklP4W2EBtKcFN1JYT/KXs2PaIeA9wc3apK1JKh3tIU2q5lBJ/+7XN3PXw7laXIknS\nrPHqZ53IM9cc0+oyDim34J1SuuQIxxPwpkMcuxq4eibqkmbCv93yIO/7/I9YvnCA3mI7/cOSJEnd\n67wzj291CYfVtW+ulFpl73CZ937uDs5cMcgNlz2XYiFaXZIkSWoDBm+pyf7mxk089PgBrnzN0w3d\nkiRpjP8GLjXR5qE9fOwbm3n5M1Zx9ur2nWMmSZLy54h3l9szXObffrCNV5y9ip66ucYHRip84tv3\ns2v/SAur6z5fu2uIgZ4iv3fBaa0uRZIktRmDd5f7/K0Pcfk//ZD9IxV+6dyTxtr/+st38+Gv3tPC\n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\n",
      "text/plain": [
       "<Figure size 864x576 with 2 Axes>"
      ]
     },
     "metadata": {
      "tags": []
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, axes = plt.subplots(2, sharex=True, figsize=(12, 8))\n",
    "fig.suptitle('Training Metrics')\n",
    "\n",
    "axes[0].set_ylabel(\"Loss\", fontsize=14)\n",
    "axes[0].plot(train_loss_results)\n",
    "\n",
    "axes[1].set_ylabel(\"Accuracy\", fontsize=14)\n",
    "axes[1].set_xlabel(\"Epoch\", fontsize=14)\n",
    "axes[1].plot(train_accuracy_results)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "u_QsWTX5V0qV"
   },
   "source": [
    "## 评估模型\n",
    "评估模型类似于训练模型。 最大的区别是示例来自单独的测试集而不是训练集。 为了公平地评估模型的有效性，用于评估模型的示例必须与用于训练模型的示例不同。\n",
    "\n",
    "测试数据集的设置类似于训练数据集的设置。 下载CSV文本文件并解析该值，然后将其打乱："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 74
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 1700,
     "status": "ok",
     "timestamp": 1559464851296,
     "user": {
      "displayName": "Will Chen",
      "photoUrl": "",
      "userId": "01179718990779759737"
     },
     "user_tz": -480
    },
    "id": "ZJPLpvmTVir7",
    "outputId": "e63593c8-2978-4d2d-af88-501d09ed62fe"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/iris_test.csv\n",
      "\r",
      "8192/573 [============================================================================================================================================================================================================================================================================================================================================================================================================================================] - 0s 0us/step\n"
     ]
    }
   ],
   "source": [
    "test_url = \"https://storage.googleapis.com/download.tensorflow.org/data/iris_test.csv\"\n",
    "\n",
    "test_fp = tf.keras.utils.get_file(fname=os.path.basename(test_url),\n",
    "                                  origin=test_url)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "FuZT638JWTA3"
   },
   "outputs": [],
   "source": [
    "test_dataset = tf.data.experimental.make_csv_dataset(\n",
    "    test_fp,\n",
    "    batch_size,\n",
    "    column_names=column_names,\n",
    "    label_name='species',\n",
    "    num_epochs=1,\n",
    "    shuffle=False)\n",
    "\n",
    "test_dataset = test_dataset.map(pack_features_vector)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "EZh0SnauWkbg"
   },
   "source": [
    "评估测试数据集上的模型\n",
    "\n",
    "与训练阶段不同，该模型仅评估测试数据的单个时期。 在下面的代码单元格中，我们迭代测试集中的每个示例，并将模型的预测与实际标签进行比较。 这用于测量整个测试集中模型的准确性。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 1700,
     "status": "ok",
     "timestamp": 1559465738675,
     "user": {
      "displayName": "Will Chen",
      "photoUrl": "",
      "userId": "01179718990779759737"
     },
     "user_tz": -480
    },
    "id": "g_FNdjbiWVuV",
    "outputId": "4f9f564c-d7e4-405f-bf30-64a35d0fc6f4"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "测试集准确率： tf.Tensor(0.96666664, shape=(), dtype=float32)\n"
     ]
    }
   ],
   "source": [
    "# 准确率统计类\n",
    "test_accuracy = tf.keras.metrics.Accuracy()\n",
    "\n",
    "for (x,y) in test_dataset:\n",
    "    logits = model(x)\n",
    "    prediction = tf.argmax(logits, axis=1, output_type=tf.int32)\n",
    "    test_accuracy(prediction, y) \n",
    "\n",
    "print('测试集准确率：', test_accuracy.result())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "b0DUmLjEZyXr"
   },
   "source": [
    "结果对比\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 538
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 1719,
     "status": "ok",
     "timestamp": 1559465856252,
     "user": {
      "displayName": "Will Chen",
      "photoUrl": "",
      "userId": "01179718990779759737"
     },
     "user_tz": -480
    },
    "id": "wsfZ1uZ0XNj4",
    "outputId": "d1e85280-095f-43ca-e0f9-8bcd6e4d27e3"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<tf.Tensor: id=164737, shape=(30, 2), dtype=int32, numpy=\n",
       "array([[1, 1],\n",
       "       [2, 2],\n",
       "       [0, 0],\n",
       "       [1, 1],\n",
       "       [1, 1],\n",
       "       [1, 1],\n",
       "       [0, 0],\n",
       "       [2, 1],\n",
       "       [1, 1],\n",
       "       [2, 2],\n",
       "       [2, 2],\n",
       "       [0, 0],\n",
       "       [2, 2],\n",
       "       [1, 1],\n",
       "       [1, 1],\n",
       "       [0, 0],\n",
       "       [1, 1],\n",
       "       [0, 0],\n",
       "       [0, 0],\n",
       "       [2, 2],\n",
       "       [0, 0],\n",
       "       [1, 1],\n",
       "       [2, 2],\n",
       "       [1, 1],\n",
       "       [1, 1],\n",
       "       [1, 1],\n",
       "       [0, 0],\n",
       "       [1, 1],\n",
       "       [2, 2],\n",
       "       [1, 1]], dtype=int32)>"
      ]
     },
     "execution_count": 35,
     "metadata": {
      "tags": []
     },
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tf.stack([y, prediction], axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "lQHhEk-CaTnq"
   },
   "source": [
    "##使用训练的模型进行预测\n",
    "\n",
    "我们已经训练了一个模型并且“证明”它对Iris物种进行分类是好的 - 但不是完美的。 现在让我们使用训练有素的模型对未标记的例子做出一些预测; 也就是说，包含特征但不包含标签的示例。\n",
    "\n",
    "在现实生活中，未标记的示例可能来自许多不同的来源，包括应用程序，CSV文件和数据源。 目前，我们将手动提供三个未标记的示例来预测其标签。 回想一下，标签号被映射到命名表示，如下所示：\n",
    "- 0: Iris setosa\n",
    "- 1: Iris versicolor\n",
    "- 2: Iris virginica"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 67
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 974,
     "status": "ok",
     "timestamp": 1559465983463,
     "user": {
      "displayName": "Will Chen",
      "photoUrl": "",
      "userId": "01179718990779759737"
     },
     "user_tz": -480
    },
    "id": "dT_bl8RKaIWx",
    "outputId": "2c4f15c2-e7f5-4e3d-be1e-9c68131d1479"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Example 0 prediction: Iris setosa (99.9%)\n",
      "Example 1 prediction: Iris versicolor (99.9%)\n",
      "Example 2 prediction: Iris virginica (99.1%)\n"
     ]
    }
   ],
   "source": [
    "predict_dataset = tf.convert_to_tensor([\n",
    "    [5.1, 3.3, 1.7, 0.5,],\n",
    "    [5.9, 3.0, 4.2, 1.5,],\n",
    "    [6.9, 3.1, 5.4, 2.1]\n",
    "])\n",
    "\n",
    "predictions = model(predict_dataset)\n",
    "\n",
    "for i, logits in enumerate(predictions):\n",
    "  class_idx = tf.argmax(logits).numpy()\n",
    "  p = tf.nn.softmax(logits)[class_idx]\n",
    "  name = class_names[class_idx]\n",
    "  print(\"Example {} prediction: {} ({:4.1f}%)\".format(i, name, 100*p))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "c8_o9k3Ganll"
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "accelerator": "GPU",
  "colab": {
   "collapsed_sections": [],
   "name": "005-custom_training_walkthrough.ipynb",
   "provenance": [],
   "version": "0.3.2"
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
